
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: StringField.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import org.dellroad.stuff.string.ParseContext;

/**
 * String field.
 *
 * <p>
 * String fields may be {@code null}, which is their default value.
 *
 * @see org.dellroad.sidekar.annotation.SidekarField
 */
public final class StringField extends AtomicField<String> {

    StringField(String name, Entity<?> entity) {
        super(name, entity, String.class);
    }

    @Override
    public void visit(FieldSwitch fieldSwitch) {
        fieldSwitch.caseStringField(this);
    }

    @Override
    public void encode(DataOutputStream output, String value) throws IOException {
        if (value == null) {
            output.write(0xff);
            return;
        }
        output.writeUTF(value);
    }

    @Override
    public String decode(DataInputStream input) throws IOException {
        byte firstByte = input.readByte();
        if (firstByte == -1)
            return null;
        return input.readUTF();
    }

    @Override
    public String toString(String value) {
        if (value == null)
            return "null";
        StringBuilder buf = new StringBuilder(value.length() + 4);
        buf.append('\"');
        for (int i = 0; i < value.length(); i++) {
            char ch = value.charAt(i);
            switch (ch) {
            case '\\':
            case '"':
                buf.append('\\').append(ch);
                break;
            case '\n':
                buf.append('\\').append('n');
                break;
            case '\r':
                buf.append('\\').append('r');
                break;
            case '\f':
                buf.append('\\').append('f');
                break;
            case '\t':
                buf.append('\\').append('t');
                break;
            default:
                buf.append(ch);
                break;
            }
        }
        buf.append('\"');
        return buf.toString();
    }

    @Override
    public String fromString(ParseContext ctx) {
        if (ctx.tryLiteral("null"))
            return null;
        ctx.expect('\"');
        StringBuilder buf = new StringBuilder();
        while (true) {
            char ch = ctx.read();
            switch (ch) {
            case '\\':
                ch = ctx.read();
                switch (ch) {
                case '\\':
                case '"':
                    buf.append(ch);
                    break;
                case 'n':
                    buf.append('\n');
                    break;
                case 'r':
                    buf.append('\r');
                    break;
                case 'f':
                    buf.append('\f');
                    break;
                case 't':
                    buf.append('\t');
                    break;
                default:
                    throw ctx.buildException("illegal backslash escape `\\" + ch + "'");
                }
                break;
            case '"':
                return buf.toString();
            default:
                buf.append(ch);
                break;
            }
        }
    }
}

